#include "pch.h"
#include "NativePointer.h"
#include <stdlib.h>
#include <malloc.h>
#include "ErrorCodes.h"

using namespace DXBase;
using namespace Platform;

inline static void* offset(void*  p, uint32 pos) { return ((byte*)p) + pos; }
inline static void* offset(size_t p, uint32 pos) { return ((byte*)p) + pos; }

MemoryBlock::MemoryBlock(size_t data, uint32 len)
	: data((void*)data), capacity(len), owningData(false)
{
	if (!data)
	{
		capacity = 0;
		owningData = true;
	}
}
MemoryBlock::MemoryBlock() : capacity(0), data(nullptr), owningData(true)
{
}
MemoryBlock::MemoryBlock(uint32 cap) : capacity(0), data(nullptr), owningData(true)
{
	ByteSize = cap;
}
MemoryBlock::~MemoryBlock()
{
	if (data && owningData)
	{
		free(data);
		data = nullptr;
		capacity = 0;
	}
}

void MemoryBlock::ByteSize::set(uint32 value)
{
	if (!owningData)
		throw ExHelper::CreateException(ErrorCodes::CanOnlyResizeOwnedData);
	if(value)
	{
		auto p = realloc(data, value);
		if (!p)
			throw ref new OutOfMemoryException();

		data = p;
		capacity = value;
	}
	else
	{
		if (data)
		{
			free(data);
			data = nullptr;
			capacity = 0;
		}
	}
}

void MemoryBlock::Memset(IPointer^ p, byte value, uint32 offset, uint32 count)
{
	if(offset >= p->ByteSize)
		return;
	if (offset + count > p->ByteSize)
		count = p->ByteSize - offset;
	memset(::offset(p->Data, offset), value, count);
}
void MemoryBlock::Memcopy(IPointer^ p, uint32 offsetSrc, IPointer^ npDst, uint32 offsetDst, uint32 count)
{
	if (count == 0)
		return;
	if (!p)
		throw ref new NullReferenceException();
	if (!npDst)
		throw ref new NullReferenceException();
	if (offsetSrc + count > p->ByteSize)
		throw ref new InvalidArgumentException();
	if (offsetDst + count > npDst->ByteSize)
		throw ref new InvalidArgumentException();

	auto pSrc = ::offset(p->Data, offsetSrc);
	auto pSE = ::offset(p->Data, offsetSrc + count);
	auto pDst = ::offset(npDst->Data, offsetDst);
	auto pDE = ::offset(npDst->Data, offsetDst + count);

	if (pSrc < pDE && pSE > pDst)
	{
		memmove(pDst, pSrc, count);
	}
	else
	{
		memcpy(pDst, pSrc, count);
	}
}

PointerView::PointerView(IPointer^ p, uint32 pos)
	: pointer(p), pos(pos), len(p->ByteSize - pos)
{
	if (!pointer)
		throw ref new Platform::NullReferenceException();
	if (pointer->ByteSize < pos + len)
		throw ExHelper::CreateException(ErrorCodes::DataTooSmall);
}
PointerView::PointerView(IPointer^ p, uint32 pos, uint32 len)
	: pointer(p), pos(pos), len(len)
{
	if (!pointer)
		throw ref new Platform::NullReferenceException();
	if (pointer->ByteSize < pos + len)
		throw ExHelper::CreateException(ErrorCodes::DataTooSmall);
}
size_t PointerView::Data::get() 
{
	if (pointer->ByteSize < pos + len)
		throw ExHelper::CreateException(ErrorCodes::DataTooSmall);
	return (size_t)::offset(pointer->Data, pos);
}
